package com.sz.core.util;

/**
 * CRC-16/MODBUS implementation utility
 * Parameters:
 * - WIDTH: 16
 * - POLY (Hex): 8005
 * - INIT (Hex): FFFF
 * - XOROUT (Hex): 0000
 * - REFIN: true
 * - REFOUT: false
 */
public class CRC16Util {

    private static final int WIDTH = 16;
    private static final int POLY = 0x8005;
    private static final int INIT = 0xFFFF;
    private static final int XOROUT = 0x0000;
    private static final boolean REFIN = true;
    private static final boolean REFOUT = false;

    // Precomputed CRC table for performance
    private static final int[] CRC_TABLE = new int[256];

    static {
        // Initialize CRC table
        for (int i = 0; i < 256; i++) {
            int crc = i;
            
            if (REFIN) {
                crc = reflect(crc, 8);
            }
            
            crc <<= 8;
            
            for (int j = 0; j < 8; j++) {
                if ((crc & (1 << (WIDTH - 1))) != 0) {
                    crc = (crc << 1) ^ POLY;
                } else {
                    crc <<= 1;
                }
            }
            
            if (REFIN) {
                crc = reflect(crc, WIDTH);
            }
            
            CRC_TABLE[i] = crc & 0xFFFF;
        }
    }

    /**
     * Calculate CRC-16/MODBUS checksum for byte array
     *
     * @param data input data
     * @return CRC-16/MODBUS checksum
     */
    public static int calculateCRC16(byte[] data) {
        int crc = INIT;

        for (byte b : data) {
            int idx;
            if (REFIN) {
                idx = (crc ^ (b & 0xFF)) & 0xFF;
            } else {
                idx = ((crc >> (WIDTH - 8)) ^ (b & 0xFF)) & 0xFF;
            }
            crc = (crc >> 8) ^ CRC_TABLE[idx];
        }

        if (REFOUT) {
            crc = reflect(crc, WIDTH);
        }

        crc ^= XOROUT;
        crc &= 0xFFFF;

        return crc;
    }

    /**
     * Calculate CRC-16/MODBUS checksum for string
     *
     * @param data input string
     * @return CRC-16/MODBUS checksum
     */
    public static int calculateCRC16(String data) {
        return calculateCRC16(data.getBytes());
    }

    /**
     * Reflect bits in a value
     *
     * @param value value to reflect
     * @param bits number of bits to reflect
     * @return reflected value
     */
    private static int reflect(int value, int bits) {
        int result = 0;
        for (int i = 0; i < bits; i++) {
            if ((value & (1 << i)) != 0) {
                result |= (1 << (bits - 1 - i));
            }
        }
        return result;
    }

    /**
     * Convert CRC value to hexadecimal string
     *
     * @param crc CRC value
     * @return hexadecimal representation
     */
    public static String toHexString(int crc) {
        return String.format("%04X", crc);
    }
}